﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace MySearch
{
    public static class ServiceTemplateOM
    {
        private static string _Code_Separator = ".";
        private static object _LockOBj = new object();

        static ServiceTemplateOM()
        {
            RefreshCacheForServiceCategory();

            RefreshCacheForServiceType();

            RefreshCacheForServiceStep();

            RefreshCacheForServiceQuestion();
        }

        #region related to ServiceCategory
        private static IDictionary<string, ServiceCategory> _DicCacheServiceCat = null;

        public static ServiceCategory GetServiceCategory(string catCode)
        {
            if (String.IsNullOrEmpty(catCode))
                return null;

            return _DicCacheServiceCat.ContainsKey(catCode) ? _DicCacheServiceCat[catCode] : null;
        }

        public static IList<ServiceCategory> ListServiceCategories()
        {
            return ListServiceCategories(true);
        }

        public static IList<ServiceCategory> ListServiceCategories(bool onlyActive)
        {
            var colTemp = onlyActive ? _DicCacheServiceCat.Values.Where(x => x.Status == ServiceStatus.Active)
                : _DicCacheServiceCat.Values;

            return new List<ServiceCategory>(colTemp);
        }

        public static void CreateServiceCategory(string userCode, string desc, string oper)
        {
            if (String.IsNullOrEmpty(userCode) || String.IsNullOrEmpty(desc))
                throw new Exception("ServiceTemplateOM.CreateServiceCategory::Invalid parameter");

            //Normalize
            userCode = NormalizeUserCode(userCode);
            bool bChange = false;

            //Create or update service_category table
            ServiceCategory existingCat = GetServiceCategory(userCode);
            ServiceCategory newCat = new ServiceCategory(userCode, desc, ServiceStatus.Active);
            if (existingCat == null)
            {
                ServiceTemplateDAO.CreateServiceCategory(newCat);
                LogUtil.LogEvent(newCat.CategoryCode, EventAction.CreateServiceCategory, oper, newCat.ToString());

                bChange = true;
            }
            else
            {
                //ServiceType attributes, not including version
                IDictionary<string, string> dicDiff = newCat.FindDifference(existingCat);
                if (dicDiff.Count > 0)
                {
                    ServiceTemplateDAO.UpdateServiceCategory(newCat);
                    LogUtil.LogEvent(newCat.CategoryCode, EventAction.UpdateServiceCategory, oper, dicDiff);

                    bChange = true;
                }
            }

            if (bChange)
            {
                RefreshCacheForServiceCategory();
            }

        }

        public static void RefreshCacheForServiceCategory()
        {
            lock (_LockOBj)
            {
                _DicCacheServiceCat = ServiceTemplateDAO.ListServiceCategories();
            }
        }
        #endregion

        #region related to ServiceType
        private static IDictionary<string, ServiceType> _DicCacheServiceType = null;

        public static ServiceType GetServiceType(string typeCode)
        {
            if (String.IsNullOrEmpty(typeCode))
                return null;

            return _DicCacheServiceType.ContainsKey(typeCode) ? _DicCacheServiceType[typeCode] : null;
        }

        public static IList<ServiceType> ListServiceTypes(string catCode)
        {
            return ListServiceTypes(catCode, true);
        }

        public static IList<ServiceType> ListServiceTypes(string catCode, bool onlyActive)
        {
            if (String.IsNullOrEmpty(catCode))
                return new List<ServiceType>();

            var colTemp = _DicCacheServiceType.Values.Where(x => x.CategoryCode == catCode);
            if (onlyActive)
                colTemp = colTemp.Where(x => x.Status == ServiceStatus.Active);

            return new List<ServiceType>(colTemp);
        }

        public static void CreateServiceType(string userCode, string desc, int verNo, string oper)
        {
            if (String.IsNullOrEmpty(userCode) || String.IsNullOrEmpty(desc) || verNo <= 0)
                throw new Exception("ServiceTemplateOM.CreateServiceType::Invalid parameter");

            //Normalize
            userCode = NormalizeUserCode(userCode);
            string parentCode = GetParentCode(userCode);
            bool bChange = false;

            //Check parent code
            if (!String.IsNullOrEmpty(parentCode))
            {
                ServiceCategory catType = GetServiceCategory(parentCode);
                if (catType == null)
                    throw new Exception("ServiceTemplateOM.CreateServiceType::Srvice Category not found");
            }

            //Create or update service_type table
            ServiceType existingType = GetServiceType(userCode);
            ServiceType newType = new ServiceType(userCode, parentCode, desc, ServiceStatus.Active);
            if (existingType == null)
            {
                ServiceTemplateDAO.CreateServiceType(newType);
                LogUtil.LogEvent(newType.TypeCode, EventAction.CreateServiceType, oper, newType.ToString());
                ServiceTemplateDAO.CreateServiceTypeVersion(newType, verNo);
                LogUtil.LogEvent(newType.TypeCode, EventAction.CreateServiceTypeVersion, oper, "Add version:" + verNo.ToString());

                bChange = true;
            }
            else
            {
                //ServiceType attributes, not including version
                IDictionary<string, string> dicDiff = newType.FindDifference(existingType);
                if (dicDiff.Count > 0)
                {
                    ServiceTemplateDAO.UpdateServiceType(newType);
                    LogUtil.LogEvent(newType.TypeCode, EventAction.UpdateServiceType, oper, dicDiff);

                    bChange = true;
                }

                //Version
                if (!existingType.Versions.Contains(verNo))
                {
                    ServiceTemplateDAO.CreateServiceTypeVersion(newType, verNo);
                    LogUtil.LogEvent(newType.TypeCode, EventAction.CreateServiceTypeVersion, oper, "Add version:" + verNo.ToString());

                    bChange = true;
                }
            }

            if (bChange)
            {
                RefreshCacheForServiceType();

                //Add notfication here
            }

        }

        public static void RefreshCacheForServiceType()
        {
            lock (_LockOBj)
            {
                _DicCacheServiceType = ServiceTemplateDAO.ListServiceTypes();
            }
        }
        #endregion

        #region related to ServiceStep object
        private static IDictionary<string, ServiceStep> _DicCacheServiceStep = null;

        public static ServiceStep GetServiceStep(string stepCode)
        {
            if (String.IsNullOrEmpty(stepCode))
                return null;

            return _DicCacheServiceStep.ContainsKey(stepCode) ? _DicCacheServiceStep[stepCode] : null;
        }

        public static ServiceStep GetServiceStep(string userCode, int verNo)
        {
            IList<ServiceStep> lst = _DicCacheServiceStep.Where(x => x.Value.StepCodeUser == userCode && x.Value.Versions.Contains(verNo))
                .Select(x => x.Value).ToList();

            if (lst.Count > 1)
                throw new Exception("ServiceTemplateOM.GetServiceStep::Duplicate service step found for " + userCode + ", Version " + verNo.ToString());

            return lst.Count > 0 ? lst[0] : null;
        }

        public static IList<ServiceStep> ListServiceSteps(string typeCode, int verNo)
        {
            return _DicCacheServiceStep.Values.Where(x => x.TypeCode == typeCode && x.Versions.Contains(verNo))
                .OrderBy(x => x.StepCodeUser).ToList();
        }

        public static void CreateServiceStep(string userCode, string desc, int verNo, string oper)
        {
            if (String.IsNullOrEmpty(userCode) || String.IsNullOrEmpty(desc) || verNo <= 0)
                throw new Exception("ServiceTemplateOM.CreateServiceStep::Invalid parameter");

            //Normalize
            userCode = NormalizeUserCode(userCode);
            string strTypeCode = GetParentCode(userCode);
            bool bChange = false;

            ServiceType objSrvType = GetServiceType(strTypeCode);
            if (objSrvType == null)
                throw new Exception("ServiceTemplateOM.CreateServiceStep::Service type not found for " + strTypeCode);

            //Find a same step bys step_code_user and description
            ServiceStep obj = _DicCacheServiceStep.Where(x => x.Value.TypeCode == strTypeCode && x.Value.StepCodeUser == userCode && x.Value.Description == desc)
                .Select(x => x.Value).FirstOrDefault();
            if (obj == null)
            {
                obj = new ServiceStep(CreateSystemCode(userCode, verNo), strTypeCode, userCode, desc);
                ServiceTemplateDAO.CreateServiceStep(obj);
                LogUtil.LogEvent(obj.StepCode, EventAction.CreateServiceStep, oper, obj.ToString());

                bChange = true;
            }

            if (!obj.Versions.Contains(verNo))
            {
                ServiceTemplateDAO.CreateServiceStepVersion(obj, verNo);
                LogUtil.LogEvent(obj.StepCode, EventAction.CreateServiceStepVersion, oper, "Add version: " + verNo.ToString());

                bChange = true;
            }

            if (bChange)
            {
                RefreshCacheForServiceStep();

                //Add notfication here
            }

        }

        public static void RefreshCacheForServiceStep()
        {
            lock (_LockOBj)
            {
                _DicCacheServiceStep = ServiceTemplateDAO.ListServiceSteps();
            }
        }
        #endregion

        #region related to ServiceQuestion object
        private static IDictionary<string, ServiceQuestion> _DicCacheServiceQues = null;

        public static ServiceQuestion GetServiceQuestion(string quesCode)
        {
            if (String.IsNullOrEmpty(quesCode))
                return null;

            return _DicCacheServiceQues.ContainsKey(quesCode) ? _DicCacheServiceQues[quesCode] : null;
        }

        public static ServiceQuestion GetServiceQuestion(string userCode, int verNo)
        {
            IList<ServiceQuestion> lst = _DicCacheServiceQues.Where(x => x.Value.QuesCodeUser == userCode && x.Value.Versions.Contains(verNo))
                .Select(x => x.Value).ToList();

            if (lst.Count > 1)
                throw new Exception("ServiceTemplateOM.GetServiceQuestion::Duplicate service question found for " + userCode + ", Version " + verNo.ToString());

            return lst.Count > 0 ? lst[0] : null;
        }

        public static IList<ServiceQuestion> ListServiceQuestions(string stepCode, int verNo)
        {
            if (String.IsNullOrEmpty(stepCode))
                return new List<ServiceQuestion>();

            return _DicCacheServiceQues.Values.Where(x => x.StepCode == stepCode && x.Versions.Contains(verNo) && String.IsNullOrEmpty(x.ParentQuesCode))
                .OrderBy(x => x.QuesCodeUser).ToList();
        }

        public static IList<ServiceQuestion> ListServiceQuestionsByParent(string parentQuesCode, string parentQuesAnsCode)
        {
            if (String.IsNullOrEmpty(parentQuesCode) || String.IsNullOrEmpty(parentQuesAnsCode))
                return new List<ServiceQuestion>();

            return _DicCacheServiceQues.Values.Where(x => x.ParentQuesCode == parentQuesCode && x.ParentQuesAnswer == parentQuesAnsCode)
                .OrderBy(x => x.QuesCodeUser).ToList();
        }

        public static void CreateServiceQuestion(string userCode, string nature, string desc, ServiceQuestionDataType dataType, bool isMandatory, int verNo, IDictionary<string, string> dicAns, string oper)
        {
            if (String.IsNullOrEmpty(userCode) || verNo <= 0)
                throw new Exception("ServiceTemplateOM.CreateServiceQuestion::Invalid parameter");

            if (String.IsNullOrEmpty(desc))
                desc = "NA";

            //Normalize
            userCode = NormalizeUserCode(userCode);
            string strStepCodeUser = GetParentCode(userCode);
            bool bChange = false;

            ServiceStep objSrvStep = GetServiceStep(strStepCodeUser, verNo);
            if (objSrvStep == null)
                throw new Exception("ServiceTemplateOM.CreateServiceQuestion::Service Step not found for " + strStepCodeUser + ", version " + verNo);

            //Create an ServiceQuestion object
            ServiceQuestion objNew = new ServiceQuestion
            {
                QuesCode = String.Empty,
                StepCode = objSrvStep.StepCode,
                QuesCodeUser = userCode,
                Nature = nature,
                Description = desc,
                DataType = dataType,
                IsMandatory = isMandatory,
                ParentQuesCode = String.Empty,
                ParentQuesAnswer = String.Empty
            };

            foreach (KeyValuePair<string, string> kv in dicAns)
                objNew.Answers.Add(NormalizeUserCode(kv.Key), kv.Value);

            //Find a same question 
            ServiceQuestion objExisting = null;
            foreach (ServiceQuestion objTemp in _DicCacheServiceQues.Where(x => x.Value.QuesCodeUser == userCode).Select(x => x.Value))
            {
                if (objNew.FindDifference(objTemp).Count == 0)
                {
                    objExisting = objTemp;
                    break;
                }
            }

            if (objExisting == null)
            {
                objNew.QuesCode = CreateSystemCode(objNew.QuesCodeUser, verNo);
                ServiceTemplateDAO.CreateServiceQuestion(objNew);
                LogUtil.LogEvent(objNew.QuesCode, EventAction.CreateServiceQuestion, oper, objNew.ToString());

                ServiceTemplateDAO.CreateServiceQuestionVersion(objNew, verNo);
                LogUtil.LogEvent(objNew.QuesCode, EventAction.CreateServiceQuestionVersion, oper, "Add version: " + verNo.ToString());

                bChange = true;
            }
            else if (!objExisting.Versions.Contains(verNo))
            {
                ServiceTemplateDAO.CreateServiceQuestionVersion(objExisting, verNo);
                LogUtil.LogEvent(objExisting.QuesCode, EventAction.CreateServiceQuestionVersion, oper, "Add version: " + verNo.ToString());

                bChange = true;
            }

            if (bChange)
            {
                RefreshCacheForServiceQuestion();

                //Add notfication here
                //NotificationUtil.NotifyRefreshCache(RefreshCacheType.ServiceQuestion, oper);
            }

        }

        public static void CreateServiceSubQuestion(string userCode, string nature, string desc, ServiceQuestionDataType dataType, bool isMandatory, int verNo, IDictionary<string, string> dicAns, string oper)
        {
            if (String.IsNullOrEmpty(userCode) || verNo <= 0)
                throw new Exception("ServiceTemplateOM.CreateServiceSubQuestion::Invalid parameter");

            if (String.IsNullOrEmpty(desc))
                desc = "NA";

            //Normalize
            userCode = NormalizeUserCode(userCode);
            string strParentAnsCodeUser = GetParentCode(userCode);
            string strParentQuesCodeUser = GetParentCode(strParentAnsCodeUser);

            bool bChange = false;

            ServiceQuestion objParentQues = GetServiceQuestion(strParentQuesCodeUser, verNo);
            if (objParentQues == null)
                throw new Exception("ServiceTemplateOM.CreateServiceSubQuestion::Service Question not found for " + strParentQuesCodeUser + ", version " + verNo);

            if (objParentQues.Answers.Where(x => x.Key == strParentAnsCodeUser).Count() != 1)
                throw new Exception("ServiceTemplateOM.CreateServiceSubQuestion::No anawer related to the question found for " + strParentAnsCodeUser + ", version " + verNo);


            //Create an ServiceQuestion object
            ServiceQuestion objNew = new ServiceQuestion
            {
                QuesCode = String.Empty,
                StepCode = objParentQues.StepCode,
                QuesCodeUser = userCode,
                Nature = nature,
                Description = desc,
                DataType = dataType,
                IsMandatory = isMandatory,
                ParentQuesCode = objParentQues.QuesCode,
                ParentQuesAnswer = strParentAnsCodeUser
            };

            foreach (KeyValuePair<string, string> kv in dicAns)
                objNew.Answers.Add(NormalizeUserCode(kv.Key), kv.Value);

            //Find a same question 
            ServiceQuestion objExisting = null;
            foreach (ServiceQuestion objTemp in _DicCacheServiceQues.Where(x => x.Value.QuesCodeUser == userCode).Select(x => x.Value))
            {
                if (objNew.FindDifference(objTemp).Count == 0)
                {
                    objExisting = objTemp;
                    break;
                }
            }

            if (objExisting == null)
            {
                objNew.QuesCode = CreateSystemCode(objNew.QuesCodeUser, verNo);
                ServiceTemplateDAO.CreateServiceQuestion(objNew);
                LogUtil.LogEvent(objNew.QuesCode, EventAction.CreateServiceQuestion, oper, objNew.ToString());

                ServiceTemplateDAO.CreateServiceQuestionVersion(objNew, verNo);
                LogUtil.LogEvent(objNew.QuesCode, EventAction.CreateServiceQuestionVersion, oper, "Add version: " + verNo.ToString());

                bChange = true;
            }
            else if (!objExisting.Versions.Contains(verNo))
            {
                ServiceTemplateDAO.CreateServiceQuestionVersion(objExisting, verNo);
                LogUtil.LogEvent(objExisting.QuesCode, EventAction.CreateServiceQuestionVersion, oper, "Add version: " + verNo.ToString());

                bChange = true;
            }


            if (bChange)
            {
                RefreshCacheForServiceQuestion();

                //Add notfication here
            }

        }

        public static void RefreshCacheForServiceQuestion()
        {
            lock (_LockOBj)
            {
                _DicCacheServiceQues = ServiceTemplateDAO.ListServiceQuestions();
            }
        }
        #endregion

        #region private method
        private static string NormalizeUserCode(string userCode)
        {
            string[] arrTemp = userCode.Split(new string[] { _Code_Separator }, StringSplitOptions.RemoveEmptyEntries);
            StringBuilder sb = new StringBuilder();

            foreach (string strTemp in arrTemp)
            {
                int nTemp = -1;
                if (!Int32.TryParse(strTemp, out nTemp) || nTemp <= 0 || nTemp >= 99)
                    throw new Exception("ServiceTemplateOM.NormalizeUserCode::Invalid format for " + userCode);

                sb.Append(".").Append(nTemp.ToString("00"));
            }

            //Remove the first "."
            sb.Remove(0, 1);

            return sb.ToString();
        }

        private static string CreateSystemCode(string userCode, int verNo)
        {
            return userCode + "_" + verNo.ToString("000");
        }

        private static string GetParentCode(string code)
        {
            int nLastIndex = code.LastIndexOf(_Code_Separator);

            return nLastIndex < 0 ? String.Empty : code.Substring(0, nLastIndex);
        }

        #endregion
    }
}
